package org.diy.traffic.sinks;

import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;

import org.diy.traffic.updates.TrafficUpdate;
import org.diy.traffic.util.TLog;

import winterwell.jtwitter.TwitterException;
import winterwell.jtwitter.TwitterRateLimitException;

/**
 * This class represents a TrafficSink which is capable of posting TrafficUpdates to a pre-defined
 * Twitter account as status updates.
 * @author Dan Greenblatt
 *
 */
public class TwitterStatusSink extends TwitterSink {

	private static String sinkType = "TWITTER_STATUS_SINK";
	
	private static HashMap<String, TwitterStatusSink> myInstances = 
		new HashMap<String, TwitterStatusSink>();
	
	public static TwitterStatusSink getInstanceByUsername(String username) {
		return myInstances.get(sinkType + "_" + username);
	}
	
	public static TwitterStatusSink createInstance(String username, String password) {
		return TwitterStatusSink.createInstance(username, password, TwitterSink.DEFAULT_RATE_LIMIT);
	}
	
	public static TwitterStatusSink createInstance(String username, String password, int rateLimit) {
		TwitterStatusSink tss = new TwitterStatusSink(username, password, rateLimit);
		myInstances.put(tss.getName(), tss);
		return tss;
	}
	
	
	public TwitterStatusSink(String username, String password) {
		this(username,password, TwitterSink.DEFAULT_RATE_LIMIT);
	}
	public TwitterStatusSink(String username, String password, int rateLimit) {
		super(username, password, rateLimit);
		this.sinkName = TwitterStatusSink.sinkType + "_" + username;
	}
	
	public static TwitterStatusSink createFromConfigData(HashMap<String, String> configParams) {
		String username = configParams.get("username");
		String password = configParams.get("password");
		if ((username == null) || (password==null)) {
			TLog.log(Level.SEVERE, "TwitterStatusSink requires username and password. Check config file.");
			return null;
		}
		
		String maxUpdates = configParams.get("maxUpdates");
		int num_updates_per_minute;
		
		if (maxUpdates == null)
			num_updates_per_minute = TwitterSink.DEFAULT_RATE_LIMIT;
		else 
			num_updates_per_minute = Integer.parseInt(maxUpdates);
		
		return new TwitterStatusSink(username, password, num_updates_per_minute);
	}
	
	
	/**
	 * This function does the bulk of the work in processing TrafficUpdates and posting them to a
	 * Twitter account as status updates.
	 * @param t The TrafficUpdate to process
	 * @return true if the update is processed, false if not because we'd be over the rate limit
	 * TODO this function shouldn't have to care about rate limits. This should be checked BEFORE forwarding
	 * 	the update on to this object. This will be cleaner, and more in keeping with the InputDaemon / Source
	 * 	side of the equation!
	 */
	@Override
	public boolean processUpdate(TrafficUpdate t) throws TwitterStatusSinkException {
		if (this.inChillaxMode()) 
			return false;
		
		if (isWithinRateLimit()) {
			try {
				String twitterized_msg = t.getMessage();
				if (twitterized_msg.length() > 140) {
					twitterized_msg = twitterized_msg.substring(0, 140);
				}
				// update Twitter status with the contents of the TrafficUpdate
				myTwitter.updateStatus(twitterized_msg);
			} catch (TwitterException te) {
				if (te.getClass() == TwitterRateLimitException.class) {
					// wait for fifteen minutes before trying again ....
					long chillaxUntil = System.currentTimeMillis() + 600000;
					TLog.log(Level.INFO, String.format("[%s] laying low until %s", this.sinkName, new Date(chillaxUntil)));
					setChillaxUntil(chillaxUntil);
					throw new TwitterStatusSinkException(te.getMessage());
				} 
				TLog.log(Level.WARNING, "TwitterStatusSink Error while updating status");
				throw new TwitterStatusSinkException(te);
			}
			//TLog.log(Level.FINEST, "****is ok to update here");
			// do any accounting necessary now that I've handled the update ....
			didHandleUpdate();
			return true;
		} else {
			TLog.log(Level.FINEST, "[" + this.sinkName+ "]" + "outside of allowable rate limit, not handling update.");
			return false;
		}
	}
	
	/**
	 * This class represents an exception state which occurs as the result of posting TrafficUpdates
	 * as status updates to a Twitter account.
	 * @author Dan Greenblatt
	 *
	 */
	public class TwitterStatusSinkException extends TrafficSinkException {
		
		public TwitterStatusSinkException() {super();}
		
		public TwitterStatusSinkException(Exception e) {super(e);}
		
		public TwitterStatusSinkException(String s) {super(s);}
	}
}
